﻿#region Namespace
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using DCT.AutoUpdate.Code.Pipelines;
using System.Threading;
using DCT.AutoUpdate.Code.Config;
using IOPath = System.IO.Path;
using System.IO;
using DCT.AutoUpdate.Code;
using DCT.AutoUpdate.Code.Downloader;
using System.Collections.Specialized;
#endregion

namespace DCT.AutoUpdate.App
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        PinelineExecutor pe = new PinelineExecutor();

        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            pe.ExitWhenError = true;
            /*
             * 执行步骤
             * 1、执行备份
             * 2、下载
             * 3、进行指定Task操作
             */
            pe.ExecutorRunInit += new EventHandler<PinelineExecutorItemsEventArgs>(pe_ExecutorRunInit);
            pe.ExecutorRunError += new EventHandler<PinelineExecutorItemsErrorEventArgs>(pe_ExecutorRunError);
            pe.ExecutorRunCompleted += new EventHandler<PinelineExecutorItemsEventArgs>(pe_ExecutorRunCompleted);
            pe.PinelineRunInit += new EventHandler<PinelineExecutorEventArgs>(pe_PinelineRunInit);
            pe.PinelineRunCompleted += new EventHandler<PinelineExecutorEventArgs>(pe_PinelineRunCompleted);
            pe.Add("备份", new BackExecutor());
            pe.Add("下载更新文件", new DownloadExecutor(this));
            pe.Add("更新程序", new PerformTaskExecutor());
            WaitCallback action = new WaitCallback(delegate{
                pe.Run();
            });
            ThreadPool.QueueUserWorkItem(action);
        }

        void pe_ExecutorRunInit(object sender, PinelineExecutorItemsEventArgs e)
        {
            this.AppendTextToStackPanel("==>[" + e.Key + "]开始");
        }

        void pe_ExecutorRunError(object sender, PinelineExecutorItemsErrorEventArgs e)
        {
            this.AppendTextToStackPanel("==>[" + e.Key + "]出现异常!!");
            App.Log.E("update", "error", e.Error);
        }
        void pe_ExecutorRunCompleted(object sender, PinelineExecutorItemsEventArgs e)
        {
            this.AppendTextToStackPanel("==>[" + e.Key + "]完成");
        }

        void pe_PinelineRunInit(object sender, PinelineExecutorEventArgs e)
        {
            this.AppendTextToStackPanel("*****更新开始*****");
        }

        void pe_PinelineRunCompleted(object sender, PinelineExecutorEventArgs e)
        {
            this.AppendTextToStackPanel("*****更新结束*****");
        }

        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            //todo
            MessageBox.Show("todo");
        }

        private void AppendTextToStackPanel(string text)
        {
            this.AppendTextToStackPanel(text, true);
        }

        private void AppendTextToStackPanel(string text, bool appendTime)
        {
            //多线程处理
            this.Dispatcher.BeginInvoke(new Action(delegate
            {
                this.stackPanel1.Children.Add(new TextBlock() { Text = (appendTime ? ("[" + GetNow() + "]") : "") + text });
            }));
        }

        private bool UpdateLastTextInStackPanel(string text)
        {
            bool succ = false;
            TextBlock tb = null;
            if (this.stackPanel1.Children.Count > 0)
            {
                tb = this.stackPanel1.Children[this.stackPanel1.Children.Count - 1] as TextBlock;
                if (tb != null)
                {
                    this.Dispatcher.BeginInvoke(new Action(delegate
                    {
                        tb.Text = text;
                    }));
                    succ = true;
                }
            }
            return succ;
        }

        private static string GetNow()
        {
            return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        }

        #region
        class BackExecutor : ExecutorSkeleton
        {
            public override bool Run(PinelineContext context)
            {
                //创建bak临时文件夹，用于rollback
                string bakFolder = IOPath.Combine(App.AppFolder, "bak");
                if (Directory.Exists(bakFolder)) {
                    //如果有则清除调
                    Directory.Delete(bakFolder, true);
                }
                Directory.CreateDirectory(bakFolder);

                //生成备份配置文件rollback.xml
                /*
                 <root>
                    <item>
                        备份文件路径
                        <file>xxx</file>
                        原本的路径，即要回滚时的路径
                        <original></original>
                    </item>
                 </root>
                 */

                StringBuilder rollbackXml = new StringBuilder();
                rollbackXml.AppendLine(XmlUtility.DEFAULT_XML_DECLARATION)
                    .Append("<root>");

                //读取配置，把文件备份
                foreach (string bakfile in App.Config.GetListVal("backups"))
                {
                    string src = AutoUpdateXmlConfigParser.GetRealPath(bakfile);
                    string fileName = System.IO.Path.GetFileName(src);
                    string target = IOPath.Combine(bakFolder, fileName);

                    if (File.Exists(src))
                    {
                        target = FileUtility.CopyFileWithRenameWhenConfict(src, target);
                        rollbackXml.AppendFormat("<item><file>{0}</file><original>{1}</original></item>"
                            , target, src);
                    }
                }

                //存储备份文件
                rollbackXml.Append("</root>");
                File.AppendAllText(IOPath.Combine(bakFolder, "rollback.xml"), rollbackXml.ToString());

                return true;
            }
        }

        class DownloadExecutor : ExecutorSkeleton
        {
            MainWindow view = null;

            public DownloadExecutor(MainWindow view)
            {
                this.view = view;
            }

            public override bool Run(PinelineContext context)
            {
                //创建"update/yyyyMMddHHmmss"临时文件夹，用于下载文件
                string updateFolder = IOPath.Combine(App.AppFolder, "update/" + DateTime.Now.ToString("yyyyMMddHHmmss"));
                if (Directory.Exists(updateFolder))
                {
                    //如果有则清除掉
                    Directory.Delete(updateFolder, true);
                }
                Directory.CreateDirectory(updateFolder);
                context.Item["updatefolder"] = updateFolder;

                //生成备份配置文件update.xml
                /*
                 <root>
                    <item>
                        <name>xxx</name>
                        备份文件路径
                        <file>xxx</file>
                    </item>
                 </root>
                 */

                StringBuilder xml = new StringBuilder();
                xml.AppendLine(XmlUtility.DEFAULT_XML_DECLARATION)
                    .Append("<root><downloads>");

                //读取配置下载
                foreach (var item in App.Config.GetListKV("downloads"))
                {
                    var dict = CollectionUtility.Convert(item);
                    string url = "";
                    if (dict.TryGetValue("url", out url) && !string.IsNullOrWhiteSpace(url))
                    {
                        string name = "";
                        if (!dict.TryGetValue("name", out name) || string.IsNullOrWhiteSpace(name))
                        {
                            name = IOPath.GetFileName(url);
                        }
                        //获取真正下载
                        string targetlocation = IOPath.Combine(updateFolder, IOPath.GetFileName(url));
                        FileUtility.RenameWhenFileExist(targetlocation, out targetlocation);

                        {
                            //文件下载
                            LocalTargetLocation ltl = new LocalTargetLocation(targetlocation);
                            SourceLocation sl = new SourceLocation(url);
                            SingleFileDownloader downloader = new SingleFileDownloader(sl, ltl);
                            downloader.EventStatusChanged.Add(delegate(SingleFileDownloader _downloader)
                            {
                                if (_downloader.Status == FileDownloadStatus.Completed)
                                {
                                    if (_downloader.DownSucc)
                                    {
                                        this.view.AppendTextToStackPanel("====>下载完成[" + url + "]"
                                            + "[speed=" + _downloader.CompletedDownloadSpeed.ToString("0.00") + "k/s]");
                                    }
                                    else {
                                        this.view.AppendTextToStackPanel("====>下载失败[" + url + "]");
                                    }
                                }
                                else if (_downloader.Status == FileDownloadStatus.Error)
                                {
                                    App.Log.E("update","download_error", _downloader.InnerException);
                                }
                                return true;
                            });
                            
                            downloader.Start();
                        }

                        xml.AppendFormat("<item><name>{0}</name><file>{1}</file></item>"
                            , name, targetlocation);
                    }
                }

                //存储配置文件
                xml.Append("</downloads></root>");
                File.AppendAllText(IOPath.Combine(updateFolder, "update.xml"), xml.ToString());

                return true;
            }
        }

        class PerformTaskExecutor : ExecutorSkeleton
        {
            public override bool Run(PinelineContext context)
            {
                IConfigParser updateFolder = new AutoUpdateXmlConfigParser();
                updateFolder.Load(File.ReadAllText(IOPath.Combine(context.Item["updatefolder"].ToString(), "update.xml")));
                NameValueCollection updates = new NameValueCollection();
                foreach (var item in updateFolder.GetListKV("downloads"))
                {
                    string name = "";
                    string val = "";
                    foreach (var kv in item)
                    {
                        if (kv.Key == "name") {
                            name = kv.Value;
                        }
                        else if (kv.Key == "file")
                        {
                            val = kv.Value;
                        }
                    }
                    updates[name] = val;
                }

                //读取配置
                foreach (var taskitem in App.Config.GetListKV("tasks"))
                {
                    var task = CollectionUtility.Convert(taskitem);
                    string type = "";
                    task.TryGetValue("type", out type);
                    switch (type.ToLower())
                    {
                        case "copy": {
                            string downloadname = "";
                            string target = "";
                            task.TryGetValue("download", out downloadname);
                            task.TryGetValue("target", out target);
                            target = AutoUpdateXmlConfigParser.GetRealPath(target);

                            List<string> files = new List<string>();

                            //todo
                            if (downloadname == "*")
                            {
                                //全部下载文件
                                files.AddRange(updates.AllKeys);
                            }
                            else if (downloadname.Contains(",")) {
                                foreach (var item in downloadname.Split(','))
                                {
                                    if (!string.IsNullOrWhiteSpace(updates[item])) {
                                        files.Add(item);
                                    }
                                }
                            }
                            else
                            {
                                if (!string.IsNullOrWhiteSpace(updates[downloadname]))
                                {
                                    files.Add(downloadname);
                                }
                            }

                            if (files.Count > 0) {
                                foreach (var file in files)
                                {
                                    string realtarget = target;
                                    string updatefile = updates[file];
                                    if (realtarget.EndsWith("#*#")) {
                                        realtarget = realtarget.Substring(0, realtarget.Length - 3);
                                        realtarget += IOPath.GetFileName(updatefile);
                                    }

                                    File.Copy(updatefile, realtarget, true);
                                    App.Log.I("update", updatefile + "=>" + realtarget);
                                }
                            }

                            break;
                        }
                    }
                }

                return true;
            }
        }
        #endregion
    }
}
